/******************************************************************************
* Copyright (c) 2007 Alexandru Scvortov
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/


#include "common.h"

#include <string.h>
#include <ctype.h>

//! Gets the next line from the file.
static int read_line() {
	++cur_line_num;
    check_and_free(current_line);

    if (!open_file)
        return 0;

    current_line = (char *)malloc(max_line_length * sizeof(char));
    if (!fgets(current_line, max_line_length, open_file)) {
        free(current_line);
        current_line = 0;
        return 0;
    }

    return 1;
}

//! @return 1, if current_line begins with '//!' or '/*!'; 0, otherwise
static int is_comment() {
    if ((!strncmp(current_line, "//!", 3)) || (!strncmp(current_line, "/*!", 3)))
        return 1;

    return 0;
}


//! Grabs a single-line comment
static void grab_single_line_comment() {
    int i;
    i = 3;
    while (current_line[i] && isspace(current_line[i]))
        ++i;
    if (!current_line[i])
        return;

    current_value = (char*)malloc((strlen(current_line) - i + 1) * sizeof(char));
    strcpy(current_value, current_line + i);
}

//! Grabs a mutli-line comment
static void grab_multi_line_comment() {
    int i;
    i = 3;
    int size;
    size = 0;
    int first_size;
    first_size = 1022;

    while (current_line[i] && isspace(current_line[i]))
        ++i;
    if (!current_line[i]) {
        current_value = (char*)malloc(1 * sizeof(char));
        current_value[0] = 0;
    } else {
        size = strlen(current_line) - i + 1;
        current_value = (char*)malloc(size * sizeof(char));
        strcpy(current_value, current_line + i);
    }

    do {
        if(!read_line())
            return;

        if (('\n' == current_line[0]) || ('\r' == current_line[0])) {
            strcat(current_value, "\n");
            continue;
        }

        i = 0;
        while (current_line[i] && isspace(current_line[i]) && (i < first_size))
            ++i;

        if (1022 == first_size)
            first_size = i;

        if (current_line[i]) {
            size += strlen(current_line) - i + 2;
            current_value = (char*)realloc(current_value, size * sizeof(char));
//             strcat(current_value, " ");
            strcat(current_value, current_line + i);
        }
    } while (!strstr(current_line, "*/"));
    char *el = strrchr(current_value, '*');
    *el = 0;
}

//! Extracts the comment from current_line, stripping it of unnecessary whitespace.
static void grab_comment() {
    check_and_free(current_value);

    if (!current_line)
        return;

    if (!strncmp(current_line, "//!", 3))
        grab_single_line_comment();
    else if (!strncmp(current_line, "/*!", 3))
        grab_multi_line_comment();
}

//! @return 1, if current_line contains a function; 0, otherwise
static int is_function() {
    if (!(('A' < current_line[0]) && (current_line[0] < 'z')) && ('_' != current_line[0]))
        return 0;

    if (!(strchr(current_line, '{') || strchr(current_line, ';')) || (!strchr(current_line, '(')) || (!strchr(current_line, ')')))
        return 0;

    char *sb = strchr(current_line, ')') + 1;
    *sb = 0;

    return 1;
}

//! Extracts the function signiture out of the current_line
static void grab_function() {
    check_and_free(current_value);

    if (!current_line)
        return;

    current_value = (char*)malloc((strlen(current_line) + 1) * sizeof(char));
    strcpy(current_value, current_line);
}

//! @return 1, if current_line is a variable; 0, otherwise
static int is_var() {
    if (!(('A' < current_line[0]) && (current_line[0] < 'z')) && ('_' != current_line[0]))
        return 0;

    if (!strchr(current_line, ';') || !strchr(current_line, ' '))
        return 0;

    return 1;
}

//! Extracts the variable out of the current_line
static void grab_var() {
    check_and_free(current_value);

    if (!current_line)
        return;

    strtok(current_line, ",;(");

    current_value = (char*)malloc((strlen(current_line) + 1) * sizeof(char));
    strcpy(current_value, current_line);
}

void scan(char *fn) {
	cur_line_num = 0;
	current_file = strrchr(fn, '/');
	if (!current_file)
		current_file = fn;
	else
		++current_file;

    open_file = fopen(fn, "r");

    if (!open_file) {
        fprintf(stderr, "Can't open %s for reading. Check permissions and try again.\n", fn);
        move_out(1);
    }

    while (read_line()) {
        if (is_comment()) {
            grab_comment();
            if (current_value)
                parse_comment();
        } else if (is_function()) {
            grab_function();
            if (current_value)
                parse_function();
        } else if (is_var()) {
            grab_var();
            if (current_value)
                parse_var();
        }
    }

    fclose(open_file);
    open_file = 0;
}
